﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Wikey.Extensibility.Enums;

namespace Wikey.Transformation 
{
    public interface IExpressionParser
    {
        Queue<Token> TokenQueue { get; }
        List<Token> Tokens { get; }
        IExpressionParser Parse( string content );
    }

    public class ExpressionParser : IExpressionParser
    {
        private int Offset = -1;
        private int Length = 0;
        private string Content = String.Empty;
        private readonly Dictionary<char, Token> Match;
        private bool IsLiteral = false;
        private StringBuilder TempStr;
        private TokenType LastTokenType;
        private bool ExpressionStart = false;
        private bool EscapeStarted = false;

        public Queue<Token> TokenQueue 
        {
            get; 
            private set; 
        }

        public List<Token> Tokens
        {
            get { return TokenQueue.ToList(); }
        }

        public ExpressionParser()
        {
            TempStr = new StringBuilder();
            TokenQueue = new Queue<Token>(4);
            Match = new Dictionary<char, Token>();
            InitializeTokens();
        }

        private void InitializeTokens()
        {
            Match.Add('{', new Token (TokenType.LeftBracket ));
            Match.Add('}', new Token(TokenType.RightBracket));
            Match.Add(':', new Token(TokenType.SemiColumn));
            Match.Add('|', new Token(TokenType.Seperator));
            Match.Add('=', new Token(TokenType.Equal));
            Match.Add('[', new Token(TokenType.LeftSquareBracket));
            Match.Add(']', new Token(TokenType.RightSquareBracket));
            //Match.Add('\"', new Token(TokenType.DoubleQuote));
            //Match.Add('\'', new Token(TokenType.SingleQuote));
        }

        
        public IExpressionParser Parse( string content )
        {
            if( String.IsNullOrEmpty(content))
            {
                return this;
            }

            Offset = 0;
            Content = content;
            Length = content.Length;

            while (Offset < Length)
            {
                Token foundToken;
                if (TokenCheck( out foundToken))
                {
                    if( foundToken.Type == TokenType.LeftSquareBracket)
                    {
                        continue;
                    }

                    Offset++;
                    LastTokenType = foundToken.Type;
                    CheckPossibleLiteral();
                    TokenQueue.Enqueue(foundToken);
                    
                    if (foundToken.Type == TokenType.LeftBracket) {
                        ExpressionStart = true;
                    }
                    else if ( foundToken.Type == TokenType.RightBracket)
                        ExpressionStart = false;

                    IsLiteral = false; 
                }
                else
                {
                    TempStr.Append(Content[Offset]);
                    Offset++;
                    IsLiteral = true;
                    
                }
            }

            if( TempStr.Length > 0 )
            {
                TokenQueue.Enqueue(
                        new Token
                            {
                                Type = TokenType.Literal , 
                                Value =  TempStr.ToString()
                            }
                    );
                ClearTempString();
            }

            return this;
        }

        private void CheckRightBreakPossibilities()
        {
            switch (TokenQueue.Last().Type)
            {
                case TokenType.Seperator:
                    break;
                case TokenType.Equal:
                    TokenQueue.Enqueue(
                        new Token
                            {
                                Type = TokenType.ParameterValue,
                                Value = TempStr != null ? TempStr.ToString() : String.Empty
                            });
                    break;
                case TokenType.SemiColumn:
                    TokenQueue.Enqueue(
                            new Token
                                {
                                    Type = TokenType.Parameter ,
                                    Value = TempStr != null ? TempStr.ToString().Trim() : String.Empty
                                }
                        );
                    break;
                case TokenType.LeftBracket: 
                    TokenQueue.Enqueue(
                            new Token
                                {
                                    Type = TokenType.Command,
                                    Value = TempStr != null ? TempStr.ToString().Trim() : String.Empty
                                }
                        );
                    
                    break;
            }
        }

        private void ClearTempString()
        {
            TempStr.Remove(0, TempStr.Length);
        }
        private bool IsEmpty
        {
            get { return TempStr.Length == 0; }
        }
        private void CheckPossibleLiteral()
        {
            if( !ExpressionStart && TempStr.Length > 0 )
            {
                TokenQueue.Enqueue( 
                        new Token
                            {
                                Type = TokenType.Literal , 
                                Value = TempStr.ToString()
                            }
                    );
                ClearTempString();
            }

            //why the hell is this here ? 
            if (!ExpressionStart || TokenQueue.Count <= 0 || IsEmpty )
            {
                return;
            }

            if (LastTokenType == TokenType.RightBracket)
            {
                CheckRightBreakPossibilities();
                ClearTempString();
                
            }

            switch (LastTokenType)
            {
                case TokenType.SemiColumn :
                    TokenQueue.Enqueue(new Token {  Type = TokenType.Command, Value = TempStr.ToString().Trim() });
                    break;
                case TokenType.Equal :
                    TokenQueue.Enqueue(new Token {  Type = TokenType.Parameter, Value = TempStr.ToString().Trim() });
                    break;
                case TokenType.Seperator :
                    TokenQueue.Enqueue(new Token { Type = TokenType.ParameterValue, Value = TempStr.ToString().Trim() });
                    break;
                case TokenType.LeftBracket :
                    break;
                default :
                    //throw new TransformationParseException("Parse validation error.");
                    break;
            }
            ClearTempString();
         }
        // TODO Need some refactoring
        private bool TokenCheck( out Token foundToken )
        {
            char currentCharacter = Content[Offset];
            foundToken = null;

            if( char.IsWhiteSpace(currentCharacter))
            {
                foundToken = Token.Space;
                return false;               
            }

            foreach (var kvp in Match)
            {
                if (kvp.Key == currentCharacter)
                {
                    if( kvp.Value.Type == TokenType.LeftSquareBracket)
                    {
                        EscapeString();
                        foundToken = kvp.Value;
                        return true;
                    }
                    foundToken = kvp.Value;
                    break;
                }
            }

            if (foundToken != null && (ExpressionStart || foundToken.Type == TokenType.LeftBracket))
            {
                if (foundToken.Type == TokenType.SemiColumn )
                {
                    return TokenQueue.Last().Type == TokenType.LeftBracket;
                }
               

                return true;
            }

            return false; 
        }

        private void EscapeString()
        {
            int currentOffset = Offset+1;
            string temp = TempStr.ToString();

            ClearTempString();

            while( Content[currentOffset] != ']' && currentOffset <= Content.Length-1)
            {
                TempStr.Append(Content[currentOffset]);
                currentOffset++;
            }

            //reset the offset
            Offset = currentOffset + 1;

            TokenQueue.Enqueue(
                    new Token
                        {
                            Type = TokenType.Parameter ,
                            Value = TempStr.ToString()
                        }
                );
            ClearTempString();
            TempStr.Append(temp);

        }

    }
}
